#include "Socket.h"

#include <unistd.h>
#include <cstring>
#include <netinet/in.h>
#include <netinet/tcp.h>

#include "InetAddress.h"
#include "../../base/log/Logging.h"

Socket::~Socket() {
    close(sockfd_);
}

void Socket::bindAddress(const InetAddress &addr) {
    if (::bind(sockfd_, addr.getSockAddr(), static_cast<socklen_t>(sizeof(struct sockaddr_in6))) == -1) {
        // 表示构建失败, 这里也会上传
        close(sockfd_);

        // 这里记得要先释放服务器的sock资源
        LOG_FATAL << "bind error : " << strerror(errno);

        // 设置服务器异常结束
    }
}

void Socket::listen() {
    if (::listen(sockfd_, 128) == -1) {
        // 监听sock上的信息, 后面的128就是允许同时接收的数量
        close(sockfd_);
        // 这里记得要先释放服务器的sock资源
        LOG_FATAL << "listen error : " << strerror(errno);
        // 设置服务器异常结束
    }
}

int Socket::accept(InetAddress *peeraddr) {
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    int clientSock = ::accept(sockfd_, (struct sockaddr *) &client, &len);
    if (clientSock == -1) {
        // 连接失败, 不调用方法了
        LOG_ERROR << "accept error" << strerror(errno);
        return -1;
    }
    peeraddr->setAddr(client);
    return clientSock;
}

struct sockaddr_in Socket::getLocalAddr(int sockfd) {
    struct sockaddr_in localaddr;
    bzero(&localaddr, sizeof localaddr);
    socklen_t addrlen = static_cast<socklen_t>(sizeof localaddr);
    if (getsockname(sockfd, (struct sockaddr *) &localaddr, &addrlen) < 0) {
        // 这里就是出错了
        LOG_ERROR << "error getLocalAddr->getsockname : " << strerror(errno);
    }
    return localaddr;
}

struct sockaddr_in Socket::getPeerAddr(int sockfd) {
    struct sockaddr_in peeraddr;
    bzero(&peeraddr, sizeof peeraddr);
    socklen_t addrlen = static_cast<socklen_t>(sizeof peeraddr);
    if (getpeername(sockfd, (struct sockaddr *) &peeraddr, &addrlen) < 0) {
        LOG_ERROR << "error getPeerAddr->getpeername : " << strerror(errno);
    }
    return peeraddr;
}

/* 下面是直接抄的muduo的, 暂时没有用处, 有用处再进行一波注释处理*/
bool Socket::getTcpInfo(struct tcp_info *tcpInfo) const {
    socklen_t len = sizeof(*tcpInfo);
    bzero(tcpInfo, len);
    return ::getsockopt(sockfd_, SOL_TCP, TCP_INFO, tcpInfo, &len) == 0;
}

bool Socket::getTcpInfoString(char *buf, int len) const {
    struct tcp_info tcpInfo;
    bool ok = getTcpInfo(&tcpInfo);
    if (ok) {
        snprintf(buf, len, "unrecovered=%u "
                           "rto=%u ato=%u snd_mss=%u rcv_mss=%u "
                           "lost=%u retrans=%u rtt=%u rttvar=%u "
                           "sshthresh=%u cwnd=%u total_retrans=%u",
                 tcpInfo.tcpi_retransmits,  // Number of unrecovered [RTO] timeouts
                 tcpInfo.tcpi_rto,          // Retransmit timeout in usec
                 tcpInfo.tcpi_ato,          // Predicted tick of soft clock in usec
                 tcpInfo.tcpi_snd_mss,
                 tcpInfo.tcpi_rcv_mss,
                 tcpInfo.tcpi_lost,         // Lost packets
                 tcpInfo.tcpi_retrans,      // Retransmitted packets out
                 tcpInfo.tcpi_rtt,          // Smoothed round trip time in usec
                 tcpInfo.tcpi_rttvar,       // Medium deviation
                 tcpInfo.tcpi_snd_ssthresh,
                 tcpInfo.tcpi_snd_cwnd,
                 tcpInfo.tcpi_total_retrans);  // Total retransmits for entire connection
    }
    return ok;
}

void Socket::shutdownWrite() {
    if (shutdown(sockfd_, SHUT_WR) < 0) {
        // 当输入SHUT_RD之后将会关闭socket的读功能
        // 输入SHUT_WR之后会关闭socket的写功能
        // 输入SHUT_RDWR会关闭socketfd的读写功能
        // : https://www.cnblogs.com/JohnABC/p/7238241.html
        LOG_SYSERR << "sockets::shutdownWrite";
    }
}

void Socket::setTcpNoDelay(bool on) {
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY,
                 &optval, static_cast<socklen_t>(sizeof optval));
}

void Socket::setReuseAddr(bool on) {
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR,
                 &optval, static_cast<socklen_t>(sizeof optval));
}

void Socket::setKeepAlive(bool on) {
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE,
                 &optval, static_cast<socklen_t>(sizeof optval));
}

// FIXME:待定研究
void Socket::setReusePort(bool on) {
    int optval = on ? 1 : 0;
    int ret = ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT,
                           &optval, static_cast<socklen_t>(sizeof optval));
    if (ret < 0 && on) {
        LOG_SYSERR << "SO_REUSEPORT failed.";
    }
}


